//
//  HelloWorldScene.mm
//  verletRopeTestProject
//
//  Created by patrick on 29/10/2010.
//  Copyright __MyCompanyName__ 2010. All rights reserved.
//


// Import the interfaces
#import "HelloWorldScene.h"
#import "BoxSP.h"
#import "GeneralDefines.h"
#import <UIKit/UIKit.h>

//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
const float32 FIXED_TIMESTEP = 1.0f / 60.0f;

// Minimum remaining time to avoid box2d unstability caused by very small delta times
// if remaining time to simulate is smaller than this, the rest of time will be added to the last step,
// instead of performing one more single step with only the small delta time.
const float32 MINIMUM_TIMESTEP = FIXED_TIMESTEP / 2.0f; //1.0f / 600.0f;  

const int32 VELOCITY_ITERATIONS = 8;
const int32 POSITION_ITERATIONS = 8;

// maximum number of steps per tick to avoid spiral of death
const int32 MAXIMUM_NUMBER_OF_STEPS = 25;

///////////////////////

// enums that will be used as tags
enum {
	kTagTileMap = 1,
	kTagBatchNode = 1,
	kTagAnimation1 = 1,
};

VPoint* ropePoint;
b2MouseJoint *_mouseJoint;
b2Body *_pointBody;
b2Fixture *_pointFixture;
b2Body *_groundBody;
// +++ Add anchor body
b2BodyDef anchorBodyDef;
// +++ Create box2d joint
b2RopeJointDef jd;

// HelloWorld implementation
@implementation HelloWorld

+(id) scene
{
	// 'scene' is an autorelease object.
	CCScene *scene = [CCScene node];
	
	// 'layer' is an autorelease object.
	HelloWorld *layer = [HelloWorld node];
	CGSize screenSize = [CCDirector sharedDirector].winSize;
	
	CCSprite * frame = [CCSprite spriteWithFile:@"windowFrame.png"];
	[frame setPosition:ccp(screenSize.width/2.0f, screenSize.height/2.0f)];
	
	CCSprite * bg = [CCSprite spriteWithFile:@"Pag5_BKG.png"];
	[bg setPosition:ccp(screenSize.width/2.0f, screenSize.height/2.0f)];
	
	[layer addChild:frame z:40];
	[layer addChild:bg z:-1];
	
	// add layer as a child to scene
	[scene addChild: layer];
	
	// return the scene
	return scene;
}

// initialize your instance here
-(id) init
{
	if( (self=[super init])) {
		
		// enable touches
		self.isTouchEnabled = YES;
		
		// enable accelerometer
		self.isAccelerometerEnabled = YES;
		
		CGSize screenSize = [CCDirector sharedDirector].winSize;
		CCLOG(@"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
		
		// Define the gravity vector.
		b2Vec2 gravity;
		gravity.Set(0.0f, -10.0f);
		
		// Do we want to let bodies sleep?
		// This will speed up the physics simulation
		bool doSleep = true;
		
		// Construct a world object, which will hold and simulate the rigid bodies.
		world = new b2World(gravity, doSleep);
		world->SetAutoClearForces(false);
		
		world->SetContinuousPhysics(YES);
		
		
		// Debug Draw functions
		m_debugDraw = new GLESDebugDraw( PTM_RATIO );
		world->SetDebugDraw(m_debugDraw);
		
		uint32 flags = 0;
		flags += b2DebugDraw::e_shapeBit;
		m_debugDraw->SetFlags(flags);		
		
		
		// Define the ground body.
		b2BodyDef groundBodyDef;
		groundBodyDef.position.Set(0, 0); // bottom-left corner
		// Create edges around the entire screen
		_groundBody = world->CreateBody(&groundBodyDef);
		b2PolygonShape groundBox;
		b2FixtureDef groundBoxDef;
		groundBoxDef.shape = &groundBox;
		groundBox.SetAsBox(([CCDirector sharedDirector].winSize.width/2.0f)/PTM_RATIO, 
						   ([CCDirector sharedDirector].winSize.height/2.0f)/PTM_RATIO);
		_groundBody->CreateFixture(&groundBoxDef);
		
		anchorBodyDef.position.Set((screenSize.width - 450.0f)/PTM_RATIO,(screenSize.height/PTM_RATIO)*0.95f); //center body on screen
		anchorBody = world->CreateBody(&anchorBodyDef);
		// +++ Add rope spritesheet to layer
		ropeSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"rope01.png" ];
		[self addChild:ropeSpriteSheet];
		// +++ Init array that will hold references to all our ropes
		vRopes = [[NSMutableArray alloc] init];
		
		//Set up sprite
		
		b2Body* temp;
		
		CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"blocks1.png" capacity:150];
		[self addChild:batch z:0 tag:kTagBatchNode];
		
		temp = [self addNewSpriteWithCoords:ccp(screenSize.width/2, screenSize.height/2 - 200)];
		
		CCLabelTTF *label = [CCLabelTTF labelWithString:@"Tap screen" fontName:@"Marker Felt" fontSize:32];
		//[self addChild:label z:0];
		//[label setColor:ccc3(0,0,255)];
		//label.position = ccp( screenSize.width/2, screenSize.height-50);
		
		[self schedule: @selector(tick:)];
		
	}
	return self;
}

-(void)afterStep:(ccTime)dt {
	// process collisions and result from callbacks called by the step
	// +++ Update rope physics
	for(uint i=0;i<[vRopes count];i++) {
		if (ropePoint && _mouseJoint) {
			[ropePoint setPos:_mouseJoint->GetTarget().x*PTM_RATIO y:_mouseJoint->GetTarget().y*PTM_RATIO];
			[ropePoint update];
		}
		[[vRopes objectAtIndex:i] update:dt];
	}
}

-(void)step:(ccTime)dt {
	float32 frameTime = dt;
	int stepsPerformed = 0;
	while ( (frameTime > 0.0) && (stepsPerformed < MAXIMUM_NUMBER_OF_STEPS) ){
		float32 deltaTime = std::min( frameTime, FIXED_TIMESTEP );
		frameTime -= deltaTime;
		if (frameTime < MINIMUM_TIMESTEP) {
			deltaTime += frameTime;
			frameTime = 0.0f;
		}
		world->Step(deltaTime,VELOCITY_ITERATIONS,POSITION_ITERATIONS);
		stepsPerformed++;
		[self afterStep:deltaTime]; // process collisions and result from callbacks called by the step
	}
	world->ClearForces ();
}

-(void) draw
{
	// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Needed states:  GL_VERTEX_ARRAY, 
	// Unneeded states: GL_TEXTURE_2D, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	//world->DrawDebugData();
	
	// restore default GL states
	glEnable(GL_TEXTURE_2D);
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	// +++ Update rope sprites
	for(uint i=0;i<[vRopes count];i++) {
		[[vRopes objectAtIndex:i] updateSprites];
	}
	
}

-(b2Body*) addNewSpriteWithCoords:(CGPoint)p
{
	CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
	CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagBatchNode];
	
	CGSize screenSize = [CCDirector sharedDirector].winSize;
	
	//We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
	//just randomly picking one of the images
	int idx = (CCRANDOM_0_1() > .5 ? 0:1);
	int idy = (CCRANDOM_0_1() > .5 ? 0:1);
	BoxSP *sprite = [BoxSP boxWithBatchNode:batch rect:CGRectMake(128 * idx,128 * idy,128,128)];
	//sprite.rotation = 1 * CC_RADIANS_TO_DEGREES(anchorBody->GetAngle());
	[batch addChild:sprite];
	
	sprite.position = ccp( p.x, p.y);
	
	// Define the dynamic body.
	//Set up a 1m squared box in the physics world
	b2BodyDef bodyDef;
	bodyDef.type = b2_dynamicBody;

	bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
	bodyDef.userData = sprite;
	sprite.box2dBody = world->CreateBody(&bodyDef);
	
	// Define another box shape for our dynamic body.
	b2PolygonShape dynamicBox;
	dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
	
	// Define the dynamic body fixture.
	b2FixtureDef fixtureDef;
	fixtureDef.shape = &dynamicBox;	
	fixtureDef.density = 1.0f;
	fixtureDef.friction = 0.1f; 
	sprite.box2dBody->CreateFixture(&fixtureDef);
	
	jd.bodyA=anchorBody; //define bodies
	jd.bodyB=sprite.box2dBody;
	jd.localAnchorA = b2Vec2(0,0); //define anchors
	jd.localAnchorB = b2Vec2(0,0);
	jd.maxLength= (sprite.box2dBody->GetPosition() - anchorBody->GetPosition()).Length(); //define max length of joint = current distance between bodies
	
	world->CreateJoint(&jd); //create joint
	// +++ Create VRope with two b2bodies and pointer to spritesheet

	VRope *newRope = [[VRope alloc] init:anchorBody body2:sprite.box2dBody spriteSheet:ropeSpriteSheet];
	[vRopes addObject:newRope];
	return sprite.box2dBody;
}

-(b2Body*) addNewSpriteWithCoords:(CGPoint)p attachedToBody:(b2Body*)b
{
	CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
	CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagBatchNode];
	
	//We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
	//just randomly picking one of the images
	int idx = (CCRANDOM_0_1() > .5 ? 0:1);
	int idy = (CCRANDOM_0_1() > .5 ? 0:1);
	BoxSP *sprite = [BoxSP boxWithBatchNode:batch rect:CGRectMake(1 * idx,1 * idy,4,4)];
	sprite.rotation = 1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
	[batch addChild:sprite];
	
	sprite.position = ccp( p.x, p.y);
	
	// Define the dynamic body.
	//Set up a 1m squared box in the physics world
	b2BodyDef bodyDef;
	bodyDef.type = b2_dynamicBody;
	
	bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
	bodyDef.userData = sprite;
	sprite.box2dBody = world->CreateBody(&bodyDef);
	
	// Define another box shape for our dynamic body.
	b2PolygonShape dynamicBox;
	dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
	
	// Define the dynamic body fixture.
	b2FixtureDef fixtureDef;
	fixtureDef.shape = &dynamicBox;	
	fixtureDef.density = 1.0f;
	fixtureDef.friction = 0.1f;
	sprite.box2dBody->CreateFixture(&fixtureDef);
	
	
	// +++ Create box2d joint
	b2RopeJointDef jd;
	
	jd.bodyA=b; //define bodies
	jd.bodyB=sprite.box2dBody;
	jd.localAnchorA = b2Vec2(0,0); //define anchors
	jd.localAnchorB = b2Vec2(0,0);
	jd.maxLength= (sprite.box2dBody->GetPosition() - b->GetPosition()).Length(); //define max length of joint = current distance between bodies
	world->CreateJoint(&jd); //create joint
	// +++ Create VRope with two b2bodies and pointer to spritesheet
	VRope *newRope = [[VRope alloc] init:b body2:sprite.box2dBody spriteSheet:ropeSpriteSheet];
	[vRopes addObject:newRope];
	return sprite.box2dBody;
}

-(void)removeRopes {
	for(uint i=0;i<[vRopes count];i++) {
		[[vRopes objectAtIndex:i] removeSprites];
		[[vRopes objectAtIndex:i] release];
	}
	[vRopes removeAllObjects];
}

-(void) tick: (ccTime) dt
{
	[self step:dt];

	
	//Iterate over the bodies in the physics world
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
	{
		if (b->GetUserData() != NULL) {
			b->SetAwake(NO);
			b->SetAngularDamping(2.0f);
			//Synchronize the AtlasSprites position and rotation with the corresponding body
			BoxSP *myActor = (BoxSP*)b->GetUserData();
			myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
			myActor.oldPos = myActor.position;
			myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
		}	
	}
	
	if (_pointBody && ropePoint) {
		_pointBody->SetAwake(NO);
		//[ropePoint setPos:(_pointBody->GetPosition().x * PTM_RATIO) y:(_pointBody->GetPosition().y * PTM_RATIO)];
		//[ropePoint update];
	}
	
}


- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{	
	static float prevX=0, prevY=0;
	
	//#define kFilterFactor 0.05f
#define kFilterFactor 1.0f	// don't use filter. the code is here just as an example
	
	float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
	float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
	
	prevX = accelX;
	prevY = accelY;
	
	// accelerometer values are in "Portrait" mode. Change them to Landscape left
	// multiply the gravity by 10
	b2Vec2 gravity( -accelY * 10, accelX * 10);
	
	world->SetGravity( gravity );
}

- (void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{	
	UITouch *touch = [touches anyObject];
	CGPoint location = [touch locationInView: [touch view]];
	CGPoint cLoc = [[CCDirector sharedDirector] convertToUI: location];
	
	ropePoint = nil;
	ropePoint = [[vRopes objectAtIndex:0] dragRopeWithPoint:cLoc];
	//[ropePoint setPos:cLoc.x y:cLoc.y];
	//[ropePoint update];
	
	

	b2BodyDef pointBodyDef;
	pointBodyDef.type = b2_dynamicBody;
	CGPoint locGL = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(locGL.x/PTM_RATIO, locGL.y/PTM_RATIO);
	pointBodyDef.position.Set(locGL.x/PTM_RATIO, locGL.y/PTM_RATIO);
	_pointBody = world->CreateBody(&pointBodyDef);
	
	b2PolygonShape pointShape;
	pointShape.SetAsBox(64/PTM_RATIO/2, 
						 64/PTM_RATIO/2);
	
	// Create shape definition and add to body
	b2FixtureDef pointShapeDef;
	pointShapeDef.shape = &pointShape;
	pointShapeDef.density = 10.0f;
	pointShapeDef.friction = 0.9f;
	pointShapeDef.restitution = 0.9f;
	_pointFixture = _pointBody->CreateFixture(&pointShapeDef);
	
    if (_pointFixture->TestPoint(locationWorld)) {
        b2MouseJointDef md;
        md.bodyA = _groundBody;
        md.bodyB = _pointBody;
        md.target = locationWorld;
        md.collideConnected = true;
        md.maxForce = 1000.0f * _pointBody->GetMass();
		
        _mouseJoint = (b2MouseJoint *)world->CreateJoint(&md);
        _pointBody->SetAwake(true);
    }
}

- (void)ccTouchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{	
	UITouch *touch = [touches anyObject];
	CGPoint location = [touch locationInView: [touch view]];
	CGPoint cLoc = [[CCDirector sharedDirector] convertToUI: location];
	//[ropePoint setPos:cLoc.x y:cLoc.y];
	//[ropePoint update];
	//NSLog(@"... (x,y) = (%.2f,%.2f)", cLoc.x, cLoc.y);
	
	if (_mouseJoint == NULL) return;
    CGPoint locGL = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(locGL.x/PTM_RATIO, locGL.y/PTM_RATIO);
    _mouseJoint->SetTarget(locationWorld);
	
}

/*- (void)ccTouchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{	
	UITouch *touch = [touches anyObject];
	CGPoint location = [touch locationInView: [touch view]];
	CGPoint cLoc = [[CCDirector sharedDirector] convertToUI: location];
	//[ropePoint setPos:cLoc.x y:cLoc.y];
	//NSLog(@"... (x,y) = (%.2f,%.2f)", cLoc.x, cLoc.y);
}*/

-(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
	
    if (_mouseJoint) {
        world->DestroyJoint(_mouseJoint);
        _mouseJoint = NULL;
    }
	
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if (_mouseJoint) {
        world->DestroyJoint(_mouseJoint);
        _mouseJoint = NULL;
    }  
	UITouch *touch = [touches anyObject];
	CGPoint location = [touch locationInView: [touch view]];
	CGPoint cLoc = [[CCDirector sharedDirector] convertToUI: location];
}


// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
	// in case you have something to dealloc, do it in this method
	delete world;
	world = NULL;
	
	delete m_debugDraw;

	// don't forget to call "super dealloc"
	[super dealloc];
}
@end
